package owg.util.editor2d.modes;

import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;

import com.jogamp.opengl.GL;

import owg.util.Calc;
import owg.util.data.Pair;
import owg.util.editor2d.ConstructorParams;
import owg.util.editor2d.Editor2D;
import owg.util.editor2d.entity.Placeable2D;
import owg.util.editor2d.operations.Operation;
import owg.util.euclidian.V2D;
import owg.util.euclidian.V3D;
import owg.util.opengl.ColorF;
import owg.util.opengl.EGL;
import owg.util.opengl.ImmediateModeWrapper;

public class BoxSelect extends Mode {
	public static enum BoxSelectMode { BOX, CIRCLE, PAINT; }
	private interface Condition2D { boolean test(double x, double y); }
	
	boolean started = false;
	V2D mouseStart = null;
	BoxSelectMode mode;
	Condition2D condition;
	boolean append = false;
	int startNumber;
	
	public BoxSelect(Editor2D e, BoxSelectMode mode) {
		super(e);
		editor.editorUI().message(mode+"SELECT...");
		startNumber = e.selectedIndices.size();
		this.mode = mode; 
	}
	@Override
	public void wheel(MouseWheelEvent e)
	{
		int paintRadius = editor.settings.paintRadius.get();
		paintRadius = (int) (paintRadius*(1-e.getWheelRotation()/4f))-e.getWheelRotation()*8;
		paintRadius = Calc.limit(paintRadius, 8, 256);
		editor.settings.paintRadius.set(paintRadius);
		if(mode == BoxSelectMode.PAINT)
			updateMessage();
	}
	
	
	
	private void updateMessage()
	{
		String mouse = format(editor.eventHandler().getMousePosition());
		if(mode == BoxSelectMode.PAINT)
			editor.editorUI().replaceMessage(mode+"SELECT ("+mouse+", r = "+editor.settings.paintRadius.get()+")");
		else
			editor.editorUI().replaceMessage(mode+"SELECT ("+(started?format(mouseStart)+" -> ":"")+mouse+")");
	}
	private String format(V2D v)
	{
		return (int)Math.round(v.x())+", "+(int)Math.round(v.y());
	}
	@Override
	public void mouse(MouseEvent e) {
		V2D mouse = editor.eventHandler().getMousePosition();
		if(e.getID() == MouseEvent.MOUSE_PRESSED)
		{
			if(e.getButton() == MouseEvent.BUTTON1 && !started)
			{
				mouseStart = mouse;
				started = true;
				editor.storeState();
				if(mode == BoxSelectMode.PAINT && !e.isShiftDown())
				{
					editor.selectedIndices.clear();
					startNumber = 0;
				}
			}
			else
				terminate(true);
		}
		else if(e.getID() == MouseEvent.MOUSE_RELEASED && e.getButton() == MouseEvent.BUTTON1 && started)
		{
			append = e.isShiftDown();
			terminate(false);
		}
		else if(e.getID() == MouseEvent.MOUSE_DRAGGED)
		{
			if(started && mode == BoxSelectMode.PAINT)
			{
				V2D v = new V2D();
				int sqDist = (int) Math.pow(editor.settings.paintRadius.get(), 2);
				
				for (int i = 0; i < editor.worldObjectsImmutable.size(); i++)
				{
					Pair<Placeable2D, ConstructorParams> p = editor.worldObjectsImmutable.get(i);
					v.set(p.a.getX(), p.a.getY());
					if(mouse.sqDistance(v) < sqDist)
						editor.selectedIndices.add(i);//Ok because HashSet
				}
			}
			updateMessage();
		}
		else if(e.getID() == MouseEvent.MOUSE_MOVED)
			updateMessage();
	}
	
	@Override
	public void render(EGL<?> egl) 
	{
		float[] color = new ColorF((byte)0xE4, (byte)0x60, (byte)0x11, (byte)0xFF).getFloat();
		
		V2D mouse = editor.eventHandler().getMousePosition();
		if(mode == BoxSelectMode.PAINT)
		{
			egl.setColor(new ColorF(color, started?0.25f:0.5f));
			
			int r = editor.settings.paintRadius.get();
			ImmediateModeWrapper<?> im = egl.immediateMode();
			im.glBegin(false, GL.GL_TRIANGLE_FAN, 2, false, 0, 0);
			im.glVertex2d(mouse.x(), mouse.y());
			for(int i = 0; i<=32; i++)
			{
				double a = 2*Math.PI*i/32f;
				double h = r*Math.cos(a);
				double v = -r*Math.sin(a);
				im.glVertex2d(mouse.x()+h, mouse.y()+v);
			}
			im.glEnd();
		}
		else if(started)
		{
			if(mode == BoxSelectMode.CIRCLE) //Circle
			{
				double px = -1, py = -1; 
				for(int i=0; i<=32; i++)
				{
					double h =  Math.abs(mouse.x()-mouseStart.x())*Math.cos(i*Math.PI/16);
					double v = -Math.abs(mouse.y()-mouseStart.y())*Math.sin(i*Math.PI/16);
					double x = mouseStart.x()+h;
					double y = mouseStart.y()+v;
					if(i != 0)
					{
						editor.lineBuffer.put(px, py, color);
						editor.lineBuffer.put(x, y, color);
					}
					px = x;
					py = y;
				}
			}
			else if(mode == BoxSelectMode.BOX) //Box
			{
				editor.lineBuffer.put(mouseStart.x(), mouseStart.y(), color);
				editor.lineBuffer.put(mouse.x(), mouseStart.y(), color);
				editor.lineBuffer.put(mouse.x(), mouseStart.y(), color);
				editor.lineBuffer.put(mouse.x(), mouse.y(), color);
				editor.lineBuffer.put(mouse.x(), mouse.y(), color);
				editor.lineBuffer.put(mouseStart.x(), mouse.y(), color);
				editor.lineBuffer.put(mouseStart.x(), mouse.y(), color);
				editor.lineBuffer.put(mouseStart.x(), mouseStart.y(), color);
			}
		}
		else
		{
			if(mode == BoxSelectMode.CIRCLE) //Circle
			{
				double a = editor.sixtystage * Math.PI/30;
				double r = 48;
				double h0 =  r*Math.cos(a);
				double v0 = -r*Math.sin(a);
				double h1 =  r*Math.cos(a+Math.PI/12);
				double v1 = -r*Math.sin(a+Math.PI/12);
				for(int i = 0; i<4; i++)
				{
					double m = Math.pow(2, i-1);
					editor.lineBuffer.put(mouse.x()+h0*m, mouse.y()+v0*m, color);
					editor.lineBuffer.put(mouse.x()+h1*m, mouse.y()+v1*m, color);
					editor.lineBuffer.put(mouse.x()-h0*m, mouse.y()-v0*m, color);
					editor.lineBuffer.put(mouse.x()-h1*m, mouse.y()-v1*m, color);
					editor.lineBuffer.put(mouse.x()-v0*m, mouse.y()+h0*m, color);
					editor.lineBuffer.put(mouse.x()-v1*m, mouse.y()+h1*m, color);
					editor.lineBuffer.put(mouse.x()+v0*m, mouse.y()-h0*m, color);
					editor.lineBuffer.put(mouse.x()+v1*m, mouse.y()-h1*m, color);
				}
			}
			else if(mode == BoxSelectMode.BOX)
			{
				V3D p = new V3D();
				p.p[0] = editor.camera.pos.x();
				p.p[1] = mouse.y();
				editor.lineBuffer.put(p, color);
				p.p[0] = editor.camera.pos.x()+editor.camera.size.x();
				p.p[1] = mouse.y();
				editor.lineBuffer.put(p, color);
				p.p[0] = mouse.x();
				p.p[1] = editor.camera.pos.y();
				editor.lineBuffer.put(p, color);
				p.p[0] = mouse.x();
				p.p[1] = editor.camera.pos.y()+editor.camera.size.y();
				editor.lineBuffer.put(p, color);
			}
		}
	}
	
	@Override
	public void terminate(boolean cancel) 
	{
		if(cancel)
			editor.editorUI().replaceMessage(mode+"SELECT Cancelled.");
		if(started)
		{
			if(cancel)
			{
				editor.applyState();//Restore
			}
			else
			{
				if(mode != BoxSelectMode.PAINT)
				{
					V2D mouse = editor.eventHandler().getMousePosition();
					V2D offset = mouse.clone().sub(mouseStart);
					V2D v = new V2D();
					
					if(!append)
					{
						editor.selectedIndices.clear();
						startNumber = 0;
					}
					
					final Condition2D c;
					if(mode == BoxSelectMode.CIRCLE)
						c = (double x, double y) -> {return v.set(x,y).sub(mouseStart).multiply(1.0/offset.x(), 1.0/offset.y()).sqLen() < 1;};
					else //Box
						c = (double x, double y) -> {return Math.abs(mouseStart.x()+offset.x()/2-x) <= 
							Math.abs(offset.x())/2 && Math.abs(mouseStart.y()+offset.y()/2-y) <= Math.abs(offset.y())/2;};
					
					for (int i = 0; i < editor.worldObjectsImmutable.size(); i++)
					{
						Pair<Placeable2D, ConstructorParams> p = editor.worldObjectsImmutable.get(i);
						if(c.test(p.a.getX(), p.a.getY()))
							editor.selectedIndices.add(i);//Ok because HashSet
					}
				}
				editor.execute(Operation.NO_OPERATION, Operation.NO_OPERATION, true, mode+"SELECT "+(editor.selectedIndices.size()-startNumber)+" objects", false, false);//Store
			}
		}
		editor.modeFinished();
	}
	
	@Override
	public void key(KeyEvent e) 
	{
		if(e.getID() == KeyEvent.KEY_PRESSED && e.getKeyCode() == KeyEvent.VK_B && !started)
		{
			//Switch to next mode
			BoxSelectMode[] vals = BoxSelectMode.values();
			mode = vals[Calc.cyclic(mode.ordinal()+(e.isShiftDown()?-1:1), vals.length)];
			editor.modeListeners.fireEvents(e.getModifiers());
			updateMessage();
		}
		else if(e.getID() == KeyEvent.KEY_PRESSED && e.getKeyCode() == KeyEvent.VK_ESCAPE)
			terminate(true);
	}
	public BoxSelectMode getMode()
	{
		return mode;
	}
	
	@Override
	public boolean shouldUpdateHighlight()
	{
		return true;
	}
}
